<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <div id="app">
        <p mv-text="msg"></p>
        <p mv-text="aaa"></p>
        <p mv-text="bb"></p>
        <p mv-text="con"></p>
        <p mv-text="msg"></p>
    </div>
    <h2 mv-text="msg"></h2>
    <input type="text">
</body>
<script>
    // 1、定义响应式对象构造器
    let Mvvm = function(options) {
        if(!(this instanceof Mvvm)){
            console.warn('Mvvm is a constrictor and should be called with the `new` keyword');
        }
        this.init(options);// 开始初始化对象
    }
    // 对象的初始化
    Mvvm.prototype.init = function(options){
        // 获取容器的DOM
        this.$el = document.querySelector(options.el);
        // 将数据仓库定义到实例的私有对象上
        this._data = options.data;
        // 2、数据的劫持实现
        this.deps();
    }

    // 2、数据劫持的实现，通过循环数据仓库完成定义


    Mvvm.prototype.deps = function(){
        // 为当前的实例创建一个监控对象 watcher
        // 存放当前实例数据对应的DOM数组
        let watcher = new Watcher();
        // 仅用于观察
        this.temp = watcher;

        // Object.defineProperty 实现仓库数据的可控定义
        for (let key in this._data) {

            watcher.setDom(this.$el,key);

            // console.log(key,":", this._data[key]);
            // 以创建的实例作为可控对象容器，将仓库变量定义到对象的跟属性上
            Object.defineProperty(this, key, {
                get(){
                    // console.log("key:", key);
                    return this._data[key];
                },
                set(nv){
                    this._data[key] = nv;
                    watcher.notify(key,nv);
                }
            });
        }
        watcher.update(this._data);
    }


    let Watcher = function(){};

    // 数据DOM数组设置
    // key 是需要操作的变量名称
    Watcher.prototype.setDom = function(rootDom,key){
        let sel = "[mv-text="+ key +"]"; // 字符串形式的属性选择器
        this[key] = rootDom.querySelectorAll(sel);
    }
    // 变量更新，通知监控 完成对应DOM集合的更新
    Watcher.prototype.notify = function(key,nv){
        let tempArr = this[key];
        for (let i = 0; i < tempArr.length; i++ ){
            // console.log(tempArr[i]);
            tempArr[i].textContent = nv;
        }
    }
     // 实例创建完成是第一个更新页面操作
    Watcher.prototype.update = function (data){
        for(let key in data){
            this.notify(key,data[key]);
        }
    }
    var vm = new Mvvm({
        el: "#app",
        data: {
            msg: "变量",
            info: "测试",
            aaa: "asdasda",
            bb:"啊啊啊",
            con:"卡咔咔咔咔咔咔"
        }
    });
    vm.msg = "hahah";
</script>

</html>